package bg.btanev.eightyfour.domain.profile.feed.processor;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;

import bg.btanev.eightyfour.domain.Tag;
import bg.btanev.eightyfour.domain.profile.feed.FeedMediaContent;
import bg.btanev.eightyfour.domain.profile.feed.FeedMediaContentDimensions;
import bg.btanev.eightyfour.domain.profile.feed.FeedMediaContentMeta;

import com.sun.syndication.feed.module.mediarss.MediaEntryModule;
import com.sun.syndication.feed.module.mediarss.types.MediaContent;
import com.sun.syndication.feed.module.mediarss.types.MediaGroup;
import com.sun.syndication.feed.module.mediarss.types.Metadata;
import com.sun.syndication.feed.module.mediarss.types.PlayerReference;
import com.sun.syndication.feed.module.mediarss.types.Reference;
import com.sun.syndication.feed.module.mediarss.types.UrlReference;
import com.sun.syndication.feed.synd.SyndEntry;

/**
 *
 * @author B.Tanev
 *
 */
public class MRssProcessor extends BaseRssModulesProcessor<FeedMediaContent, MediaEntryModule> {
	/**
	 * the namespace URI of the MediaRSS specification as hosted by yahoo: <em>http://search.yahoo.com/mrss/</em>
	 */
	private final static String MRSS_URI = "http://search.yahoo.com/mrss/";

	private Collection<FeedMediaContent> globalMediaContent;
	private Collection<FeedMediaGroup> globalMediaGroups;

	private final Object moduleParserLocker = new Object();
	private boolean moduleParsed;

	public MRssProcessor(final SyndEntry feedEntry) {
		super(feedEntry);
	}

	@Override
	protected String[] getModuleNamespaces() {
		return new String[] { MRSS_URI };
	}

	@Override
	protected Collection<FeedMediaContent> processModuleInternal(final MediaEntryModule rssModule) {
		Validate.notNull(rssModule);

		if (!moduleParsed) {
			synchronized (moduleParserLocker) {
				if (!moduleParsed) {
					Collection<FeedMediaContent> feedMediaContent = null;
					Collection<FeedMediaGroup> feedMediaGroups = null;

					MediaContent[] mediaContents = rssModule.getMediaContents();
					if (!ArrayUtils.isEmpty(mediaContents)) {
						feedMediaContent = processMediaContent(mediaContents);
					}

					MediaGroup[] mediaGroups = rssModule.getMediaGroups();
					if (!ArrayUtils.isEmpty(mediaGroups)) {
						feedMediaGroups = processMediaGroups(mediaGroups);
					}

					this.globalMediaContent = feedMediaContent;
					this.globalMediaGroups = feedMediaGroups;

					moduleParsed = true;
				}
			}
		}

		return null;
	}

	public Collection<FeedMediaContent> getGlobalMediaContent() {
		if (!moduleParsed) {
			throw new IllegalStateException("Module not processed yet. You must call processModule() method first.");
		}

		return globalMediaContent;
	}

	public Collection<FeedMediaGroup> getGlobalMediaGroups() {
		if (!moduleParsed) {
			throw new IllegalStateException("Module not processed yet. You must call processModule() method first.");
		}

		return globalMediaGroups;
	}

	private Set<FeedMediaGroup> processMediaGroups(final MediaGroup[] mediaGroups) {
		assert (!ArrayUtils.isEmpty(mediaGroups));

		Set<FeedMediaGroup> extractedMediaGroups = new HashSet<FeedMediaGroup>(mediaGroups.length);

		for (MediaGroup mediaGroup : mediaGroups) {
			String title = null;
			String description = null;
			Set<Tag> tags = null;

			Metadata metadata = mediaGroup.getMetadata();
			if (metadata != null) {
				title = metadata.getTitle();
				description = metadata.getDescription();

				tags = processTags(metadata);
			}

			Set<FeedMediaContent> mediaContentItems = extractMediaContentItemsFromGroup(mediaGroup, title, description);

			if (mediaContentItems == null || mediaContentItems.size() == 0) {
				continue;
			}

			extractedMediaGroups.add(new FeedMediaGroup(mediaContentItems, tags));
		}

		if (extractedMediaGroups.isEmpty()) {
			extractedMediaGroups = null;
		} else {
			extractedMediaGroups = Collections.unmodifiableSet(extractedMediaGroups);
		}

		return extractedMediaGroups;
	}

	private Set<Tag> processTags(final Metadata metadata) {
		assert (metadata != null);

		Set<Tag> tags = null;

		String[] keywords = metadata.getKeywords();
		if (!ArrayUtils.isEmpty(keywords)) {
			tags = new HashSet<Tag>(keywords.length);
			for (String keyword : keywords) {
				if (keyword != null && keyword.trim().length() > Tag.MIN_TAG_LENGTH) {
					tags.add(Tag.createTag(keyword));
				}
			}
		}

		return tags;
	}

	private Set<FeedMediaContent> extractMediaContentItemsFromGroup(final MediaGroup mediaGroup, final String title,
			final String description) {

		assert (mediaGroup != null);

		final MediaContent[] mediaContents = mediaGroup.getContents();
		if (ArrayUtils.isEmpty(mediaContents)) {
			return null;
		}

		return processMediaContent(mediaContents, title, description);
	}

	private Set<FeedMediaContent> processMediaContent(final MediaContent[] mediaContents) {
		return processMediaContent(mediaContents, null, null);
	}

	private Set<FeedMediaContent> processMediaContent(final MediaContent[] mediaContents, final String title,
			final String description) {

		assert (!ArrayUtils.isEmpty(mediaContents));

		Set<FeedMediaContent> extractedMediaContent = new HashSet<FeedMediaContent>(mediaContents.length);

		for (MediaContent mediaContent : mediaContents) {
			String url = null;
			String type = mediaContent.getType();

			//media content placeholder
			Integer width = null;
			Integer height = null;
			FeedMediaContentDimensions dimentions = null;

			//trying to extract media content from media:player if exists
			PlayerReference player = mediaContent.getPlayer();
			if (player != null) {
				url = player.getUrl() != null ? player.getUrl().toString() : null;

				width = player.getWidth();
				height = player.getHeight();
				if ((width != null && height != null) && (width.intValue() > 0 && height.intValue() > 0)) {
					dimentions = new FeedMediaContentDimensions(width.intValue(), height.intValue());
				}
			}

			//extracting media content data from the media:content
			if (StringUtils.isEmpty(url)) {
				Reference reference = mediaContent.getReference();
				if (reference != null && reference instanceof UrlReference) {
					UrlReference urlRef = (UrlReference) reference;
					url = urlRef.getUrl().toString();
				}
			}

			if (dimentions == null) {
				width = mediaContent.getWidth();
				height = mediaContent.getHeight();
				if ((width != null && height != null) && (width.intValue() > 0 && height.intValue() > 0)) {
					dimentions = new FeedMediaContentDimensions(width.intValue(), height.intValue());
				}
			}

			if ((!(StringUtils.isEmpty(url))) && (!(StringUtils.isEmpty(type)))) {
				FeedMediaContentMeta meta = null;
				if ((!StringUtils.isEmpty(title)) || (!StringUtils.isEmpty(description))) {
					meta = new FeedMediaContentMeta(title, description);
				}
				extractedMediaContent.add(new FeedMediaContent(url, type, meta, dimentions));
			}
		}

		if (extractedMediaContent.isEmpty()) {
			extractedMediaContent = null;
		} else {
			extractedMediaContent = Collections.unmodifiableSet(extractedMediaContent);
		}

		return extractedMediaContent;
	}

	public class FeedMediaGroup {
		private final Set<FeedMediaContent> mediaContentItems = new HashSet<FeedMediaContent>();
		private final Set<Tag> tags = new HashSet<Tag>();

		public FeedMediaGroup(Collection<FeedMediaContent> mediaContentItems, Collection<Tag> tags) {
			if (mediaContentItems != null) {
				this.mediaContentItems.addAll(mediaContentItems);
			}

			if (tags != null) {
				this.tags.addAll(tags);
			}
		}

		public Set<FeedMediaContent> getMediaContentItems() {
			return Collections.unmodifiableSet(mediaContentItems);
		}

		public Set<Tag> getTags() {
			return Collections.unmodifiableSet(tags);
		}
	}
}
